package com.localjoycommunity.cpdj.project.system.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.localjoycommunity.cpdj.common.CodeMsg;
import com.localjoycommunity.cpdj.common.R;
import com.localjoycommunity.cpdj.common.constant.Constants;
import com.localjoycommunity.cpdj.common.core.lang.UUID;
import com.localjoycommunity.cpdj.common.enums.RegisterStatus;
import com.localjoycommunity.cpdj.common.exception.GlobalException;
import com.localjoycommunity.cpdj.common.interceptor.help.NeedLogin;
import com.localjoycommunity.cpdj.common.utils.AqieUtils;
import com.localjoycommunity.cpdj.common.utils.SecurityUtils;
import com.localjoycommunity.cpdj.framework.redis.RedisCache;
import com.localjoycommunity.cpdj.framework.web.controller.BaseController;
import com.localjoycommunity.cpdj.framework.web.domain.AjaxResult;
import com.localjoycommunity.cpdj.project.system.domain.User;
import com.localjoycommunity.cpdj.project.system.domain.vo.DecryptRequest;
import com.localjoycommunity.cpdj.project.system.domain.vo.PhoneInfoVo;
import com.localjoycommunity.cpdj.project.system.domain.vo.WeixinVO;
import com.localjoycommunity.cpdj.project.system.service.ISmsService;
import com.localjoycommunity.cpdj.project.system.service.IUserService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.util.concurrent.TimeUnit;

/**
 * @author aqie
 * @date 2020-11-02 8:23
 * @function 前端登录退出
 */
@RequestMapping("app")
@RestController
@Slf4j
public class AuthController extends BaseController {

    @Autowired
    private IUserService userService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ISmsService smsService;

    @Value("${weixin.appId}")
    private String appId;

    @Value("${weixin.secret}")
    private String secret;

    private String APPLET_CODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";


    private ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 用户账号密码注册
     * @param user
     * @return
     */
    @PostMapping("registerByPassword")
    public R<WeixinVO> registerByPassword(@RequestBody User user){
        // todo 根据openid 查询是否绑定过用户
        User one = userService.selectUserByOpenid(user.getOpenid());
        if(one != null){
            return R.error("微信已注册 请直接登录");
        }
        // 校验短信验证码
        String code = smsService.getSmsCode(user.getPhone());
        if (user.getCode() == null || !user.getCode().equals(code)){
            return R.error("短信验证码错误");
        }
        User user1 = userService.registerByPassword(user);
        WeixinVO weixinVO = new WeixinVO();
        weixinVO.setIsRegistered(RegisterStatus.YES.getCode());
        weixinVO.setUser(user1);
        String sessionKey = UUID.fastUUID().toString();
        weixinVO.setSession_key(sessionKey);
        // 保存用户信息到缓存
        redisCache.setCacheObject(sessionKey, weixinVO,24, TimeUnit.DAYS);
        return R.success(weixinVO);
    }

    /**
     * 2. 用户账号密码登录
     * @param phone
     * @param pwd
     * @param req
     * @return
     */
    @GetMapping("loginByPassword")
    public R<WeixinVO> loginByPassword(String phone, String pwd, HttpServletRequest req){
        // todo 查询缓存 直接返回
        WeixinVO cacheWeixinVO = getCacheWeixinVO(req);
        if (cacheWeixinVO != null) {
            return R.success(cacheWeixinVO);
        }

        String sessionKey;
        User user = userService.loginByPassword(phone, pwd);
        WeixinVO weixinVO = new WeixinVO();
        weixinVO.setIsRegistered(RegisterStatus.YES.getCode());
        weixinVO.setUser(user);
        sessionKey = UUID.fastUUID().toString();
        weixinVO.setSession_key(sessionKey);
        // 保存用户信息到缓存
        redisCache.setCacheObject(sessionKey, weixinVO,24, TimeUnit.DAYS);
        return R.success(weixinVO);
    }

    @NeedLogin
    @PostMapping("modifyPassWord")
    public R<Integer> modifyPassWord(String pwd,HttpServletRequest req){
        User user = getUser(req);
        user.setPassword(SecurityUtils.encryptPassword(pwd));
        int i = userService.updateUser(user);
        return R.success(i);
    }

    public WeixinVO getCacheWeixinVO(HttpServletRequest req) {
        String header = req.getHeader(Constants.SESSION_KEY);
        if (StringUtils.isBlank(header)){
            return null;
        }
        WeixinVO cacheWeixinVO = redisCache.getCacheObject(header);
        if (cacheWeixinVO != null) {
            return cacheWeixinVO;
        }
        return null;
    }

    /**
     * 3. 小程序登录
     * @return
     */
    @GetMapping("loginByApplet")
    public R<WeixinVO> loginByApplet(String openid, HttpServletRequest req) {
        WeixinVO cacheWeixinVO = getCacheWeixinVO(req);
        if (cacheWeixinVO != null) {
            return R.success(cacheWeixinVO);
        }
        // 点微信登录 携带openid 查数据库 如果有数据直接跳个人中心
        User user = userService.selectUserByOpenid(openid);
        if(user == null){
            return R.error("该微信用户未注册");
        }
        WeixinVO weixinVO = new WeixinVO();
        weixinVO.setIsRegistered(RegisterStatus.YES.getCode());
        weixinVO.setUser(user);
        String sessionKey = UUID.fastUUID().toString();
        weixinVO.setSession_key(sessionKey);
        redisCache.setCacheObject(sessionKey, weixinVO,24, TimeUnit.DAYS);
        return R.success(weixinVO);
    }

    @GetMapping("logout")
    public void logout(HttpServletRequest req) {
        String sessionKey = req.getHeader(Constants.SESSION_KEY);
        log.info("{}",  sessionKey);
        if (StringUtils.isNotEmpty(sessionKey)) {
            redisCache.deleteObject(sessionKey);
        }
    }


    /**
     * @Deprecated
     * @param code
     * @param req
     * @return
     */
    @GetMapping("loginApplet/{code}")
    public R<WeixinVO> loginApplet(@PathVariable String code, HttpServletRequest req){
        String sessionKey = req.getHeader(Constants.SESSION_KEY);
        log.info("{} {}", code, sessionKey);
        if (StringUtils.isNotEmpty(sessionKey)) {
            redisCache.deleteObject(sessionKey);
        }
        String url = String.format(APPLET_CODE_TO_SESSION_URL, appId, secret, code);
        String result = restTemplate.getForObject(url, String.class);

        WeixinVO weixinVo = null;
        try {
            weixinVo = objectMapper.readValue(result, WeixinVO.class);
            log.info("weixinVo : {}", weixinVo);
        } catch (IOException e) {
            log.error("ERROR",  e);
            return R.error(e.getMessage());
        }
        if (weixinVo != null  && StringUtils.isNotBlank(weixinVo.getOpenid())){
            User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenid,weixinVo.getOpenid()));
            if (user != null) {
                weixinVo.setIsRegistered(RegisterStatus.GOD.getCode());
                weixinVo.setUser(user);
            } else {
                weixinVo.setIsRegistered(RegisterStatus.NO.getCode());
            }
            sessionKey = weixinVo.getSession_key();
            // 保存用户信息到缓存
            redisCache.setCacheObject(sessionKey, weixinVo,24, TimeUnit.DAYS);

            return R.success(weixinVo);
        }else {
            return R.error(weixinVo.getErrmsg() + weixinVo.getErrcode());
        }
    }

    /**
     * 微信登录 用户认证
     * @Deprecated
     * @param req
     * @param user 必须传入手机号
     * @return
     */
    @PostMapping("/auth")
    @ApiOperation(value = "用户认证", notes = "用户认证")
    public AjaxResult auth(HttpServletRequest req, @RequestBody User user) {
        String sessionKey = req.getHeader(Constants.SESSION_KEY);
        WeixinVO weixinVO = redisCache.getCacheObject(sessionKey);
        String openId = weixinVO.getOpenid();
        // 用户已经 微信 + 手机登录
        if (RegisterStatus.GOD.getCode().equals(weixinVO.getIsRegistered())){
            throw new GlobalException(CodeMsg.USER_ALREADY_REGISTER_ERROR);
        }
        // 用户已经手机注册
        if (RegisterStatus.YES.getCode().equals(weixinVO.getIsRegistered())){
            // do nothing
        }

        // 用户第一次注册
        user.setOpenid(openId);
        userService.bindUser(user);
        weixinVO.setUser(user);
        weixinVO.setIsRegistered(RegisterStatus.GOD.getCode());
        redisCache.setCacheObject(sessionKey, weixinVO,24, TimeUnit.DAYS);
        return AjaxResult.success(weixinVO);
    }

    @PostMapping("/decryptPhone")
    public AjaxResult decryptPhone(@RequestBody @Validated DecryptRequest request, HttpServletRequest req) throws
            InvalidAlgorithmParameterException {
        String decryptData = getString(request, req);
        PhoneInfoVo phoneInfo = JSON.parseObject(decryptData, new TypeReference<PhoneInfoVo>() {
        });
        return AjaxResult.success(phoneInfo);
    }

    private String getString(DecryptRequest request, HttpServletRequest req) throws InvalidAlgorithmParameterException {
        String sessionKeyFront = req.getHeader(Constants.SESSION_KEY);
        WeixinVO weixinVO = redisCache.getCacheObject(sessionKeyFront);
        String iv = request.getIv();
        String encryptData = request.getEncryptedData();
        String sessionKey = weixinVO.getSession_key();
        // 检验签名是否一致, 只针对明文数据        String iv2 = AqieUtils.sha1(rawData + sessionKey)
        // 数据解密
        String decryptData = AqieUtils.decrypt(encryptData, sessionKey, iv);
        return decryptData;
    }


    @GetMapping("user")
    @NeedLogin
    public R<User> getUserInfo(HttpServletRequest req){
        Integer userId = getUserId(req);
        User user = userService.selectUserById(userId);
        return R.success(user);
    }
    /**
     * app 更新用户信息
     * @param user
     * @param req
     * @return
     */
    @PutMapping("user")
    @NeedLogin
    public R<Integer> appletUserEdit(@RequestBody User user, HttpServletRequest req){
        Integer userId = getUserId(req);
        user.setId(userId);
        int i = userService.updateUser(user);
        return R.success(i);
    }

    @GetMapping("hello")
    public R<String> hello(String key,String value){
        redisCache.setCacheObject(key,value);
        String name = redisCache.getCacheObject("name");
        return R.success("hello " + name);
    }


    @NeedLogin
    @GetMapping("index")
    public R<String> index() {
        return R.success("index need login");
    }
}
